package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class HotArticleServiceImpl implements IHotArticleService {

    @Autowired
    private IApArticleService articleService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 定时每天计算文章分值
     */
    @Override
    public void compute() {
        // 每天1点执行
        // 查询从0点往前推5天的所有文章(已上架已发布的文章)
        Date now = new Date();
        // 获取当前的0点时间
        Date to = new Date(now.getYear(), now.getMonth(), now.getDate());
        // 获取前5天的时刻
        Date from = new Date(to.getTime() - 5 * 24 * 60 * 60 * 1000);
        LambdaQueryWrapper<ApArticle> query = new LambdaQueryWrapper<>();
        query.eq(ApArticle::getIsDelete, false);
        query.eq(ApArticle::getIsDown, false);
        query.ge(ApArticle::getPublishTime, from);
        query.lt(ApArticle::getPublishTime, to);
        List<ApArticle> list = articleService.list(query);
        for (ApArticle article : list) {
            // 计算文章的分值
            int score = computeScore(article);
            // 按照分值排序把数据存放到redis中(APP首页和每个频道首页)
            // zset 支持按照分数排序
            System.out.println(article);
            System.out.println("分值为: " + score);
            String key = "hot_article_first_page_0";    // 首页名称
            // 前端展示列表时有: id 标题  封面  作者名称 封面类型 静态地址页
            ApArticle cache = new ApArticle();
            cache.setId(article.getId());
            cache.setTitle(article.getTitle());
            cache.setImages(article.getImages());
            cache.setAuthorId(article.getAuthorId());
            cache.setAuthorName(article.getAuthorName());
            cache.setLayout(article.getLayout());
            cache.setStaticUrl(article.getStaticUrl());
            // 注意,生成的json最好是不会发生变化的
            String value = JSON.toJSONString(cache);
            redisTemplate.opsForZSet().add(key, value, score);
            redisTemplate.expire(key, 1, TimeUnit.DAYS); // 1天的有效期

            // 往每个频道存放缓存数据
            String keyChannel = "hot_article_first_page_" + article.getChannelId();    // 每个频道首页
            redisTemplate.opsForZSet().add(keyChannel, value, score);
            redisTemplate.expire(keyChannel, 1, TimeUnit.DAYS); // 1天的有效期
        }
    }

    /**
     * 文章实时数据更新
     *
     * @param message
     */
    @Override
    public void update(ArticleStreamMessage message) {

        // 根据文章id查询文章
        ApArticle article = articleService.getById(message.getArticleId());
        ApArticle cache = new ApArticle();
        cache.setId(article.getId());
        cache.setTitle(article.getTitle());
        cache.setImages(article.getImages());
        cache.setAuthorId(article.getAuthorId());
        cache.setAuthorName(article.getAuthorName());
        cache.setLayout(article.getLayout());
        cache.setStaticUrl(article.getStaticUrl());
        // 注意,生成的json最好是不会发生变化的
        String value = JSON.toJSONString(cache);

        // 计算文章的增量分值
        int scorePlus = computeScore(message);
        // 判断redis中是否有这篇文章,可以根据key 来查询分数
        String key = "hot_article_first_page_0";    // 首页名称
        Double score = redisTemplate.opsForZSet().score(key, value);
        // 有的话加上增量的分值
        if (score != null) {
            redisTemplate.opsForZSet().incrementScore(key, value, scorePlus);
        } else {
            // 没有的话先计算历史分值,再加上增量的分值
            int hisScore = computeScore(article);
            // 更新redis中的数据
            redisTemplate.opsForZSet().add(key, value, hisScore + scorePlus);
        }
        // 每个频道的首页也需要更新  todo

        // 更新数据库中的行为操作
        LambdaUpdateWrapper<ApArticle> update = new LambdaUpdateWrapper<>();
        update.eq(ApArticle::getId, article.getId());
        update.setSql("views = views +" + message.getView());
        update.setSql("likes = likes +" + message.getLike());
        update.setSql("comment = comment +" + message.getComment());
        update.setSql("collection = collection +" + message.getCollect());
        articleService.update(update);
    }

    /**
     * 计算当日的增量分数  当日的操作整体权重*3
     *
     * @param message
     * @return
     */
    private int computeScore(ArticleStreamMessage message) {
        int score = 0;
        score += message.getView() * 1 * 3;
        score += message.getLike() * 3 * 3;
        score += message.getComment() * 5 * 3;
        score += message.getCollect() * 8 * 3;
        return score;
    }

    private int computeScore(ApArticle article) {
        int score = 0;
        // 阅读 +1  点赞 +3  评论 +5  收藏 +8
        if (article.getViews() != null) {
            score += article.getViews() * 1;
        }
        if (article.getLikes() != null) {
            score += article.getLikes() * 3;
        }
        if (article.getComment() != null) {
            score += article.getComment() * 5;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * 8;
        }
        return score;
    }
}
